package com.vinfai.thread.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
 * CountDownLatch：一个同步辅助类，能让一组线程一直等待，直到其他线程中的正在执行的操作完成。
 * A synchronization aid that allows one or more threads to wait until 
 * a set of operations being performed in other threads completes. 
 * 
 * @author vinfai
 */
public class CountDownLatchTest {

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
//		long t = new CountDownLatchTest().timeCost(10,null);
//		System.out.println(t);
		int size = 1000000;
		List<Long> items = new ArrayList<Long>();
		for(int i=0;i<size;i++){
			items.add(new Long(i+1));
		}
		//性能比直接相加还差100倍，只是用于演示使用方法
		long st1 = System.nanoTime();
		Long val = new CountDownLatchTest().multiCalculateAndSum(items, 20);
		System.out.println(val);
		System.out.println(System.nanoTime()-st1);
		
		long st = System.nanoTime();
		long sum = 0L;
		for(int i=0;i<size;i++){
			sum += (i+1);
		}
		System.out.println(sum);
		System.out.println(System.nanoTime()-st);
	}
	
	public long timeCost(int times,Runnable task) throws InterruptedException{
		
		final CountDownLatch startLatch = new CountDownLatch(1);
		final CountDownLatch overLatch  = new CountDownLatch(times);
		
		for(int i=0;i<10;i++){
			final int j = i;
			new Thread(){
				public void run(){
					try {
						System.out.println(Thread.currentThread().getName()+"$"+j+" start");
						startLatch.await(); //一直等待直到[operations]完成
						System.out.println(Thread.currentThread().getName()+"$"+j+"end..................");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}finally{
						//完成一个线程，count--;until zero.
						overLatch.countDown();
					}
				}
			}.start();
		}
		Thread.sleep(1000);
		//
		//doSomthing(); [operations]
		System.out.println("Main Thread is ready now...");
		startLatch.countDown(); //上面的操作[operations]完成前，线程组一直处于阻塞状态.
		long startTime = System.nanoTime();
		overLatch.await(); //等待其他线程完成时，才进行执行主线程
		return System.nanoTime()-startTime;
	}
	
	
	/**
	 * 将数据分成N部分，每部分分别相加，最后求和
	 * @return
	 * @throws InterruptedException 
	 */
	public Long multiCalculateAndSum(List<Long> items,int splitNum) throws InterruptedException{
		if(items==null) throw new IllegalArgumentException();
		if(items.size()<splitNum) splitNum = items.size();
		int k = items.size()/splitNum;
		CountDownLatch doneSingle = new CountDownLatch(splitNum);
		ExecutorService pool = Executors.newFixedThreadPool(splitNum);
		long sum = 0L;
		//List<Long> rs = new ArrayList<Long>();
		List<Future<Long>> futures = new ArrayList<Future<Long>>();
		for(int i=0;i<splitNum;i++){
			int endIndex = (i+1)*k;
			if(endIndex>items.size()) endIndex = items.size();
			List<Long> threadItems = items.subList(i*k, endIndex);
			Worker task = new Worker(doneSingle, threadItems);
			/*// 这样写每次只执行一个线程,并没有实现线程池的使用.
			Future<Long> future = pool.submit(task);
			Long threadSum = 0L;
			try {
				threadSum = future.get();
				System.out.println("get rs:"+threadSum);
				//threadSum = future.get(1, TimeUnit.SECONDS);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			} 
			//sum +=threadSum;
			//为了测试CountDownLatch写下面的内容
			rs.add(threadSum);*/
			futures.add(pool.submit(task));
		}
		doneSingle.await();
	
		/*for(int i=0;i<rs.size();i++){
			sum +=rs.get(i);
		}
		*/
		for(int i=0;i<futures.size();i++){
			try {
				sum +=futures.get(i).get();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}
		pool.shutdown(); //必须关闭线程池
		return sum;
	}
	
	class Worker implements Callable{
		private final CountDownLatch doneSignle ;
		private List<Long> items;
		public Worker(CountDownLatch single,List<Long> items){
			this.items = items;
			doneSignle = single;
		}
		private Long sum = 0L;
		@Override
		public Long call() throws Exception {
			try{
				for(int i=0;i<items.size();i++){
					sum += items.get(i);
				}
				return sum;
			}finally{
				System.out.println(Thread.currentThread().getName()+" is end now."+sum+ "  : "+doneSignle.getCount());
				doneSignle.countDown();
				System.out.println(doneSignle.getCount());
			}
		}
	}
	

}
